import {Cell, Edge, Graph, NodeView, Rectangle} from "@antv/x6";

// @ts-ignore
import './definition.css'
import {Snapline} from "@antv/x6-plugin-snapline";
import {Transform} from "@antv/x6-plugin-transform";
import {MiniMap} from '@antv/x6-plugin-minimap'
import {Component} from "react";
import {message} from "antd";
import {Export} from "@antv/x6-plugin-export";
import {History} from '@antv/x6-plugin-history'
import {State} from "./types.tsx";
import load_external from "./external.tsx";
import {isNumeric, nth} from "../../../../utils/util.tsx";

const LABEL_POSITION_DISTANCE = 25

/** 主体公司 */ export const SHAPE_RECT = 'cm'
// /** 家族公司 */ export const SHAPE_FAMILY_COM = 'fcm'
// /** 平台公司 */ export const SHAPE_PLATFORM_COM = 'pcm'
// /** 其他投资板块公司 */ export const SHAPE_OTHER_COM = 'ocm'
// /** 投资方 */ export const SHAPE_INVESTOR = 'inv'
// /** 员工持股平台 */ export const SHAPE_SHARE_HOLDER = 'shd'
// /** 拟新设公司 */ export const SHAPE_NEW_COM = 'ncm'
// /** 拟注销公司 */ export const SHAPE_CANCELED_COM = 'ccm'

/** 自然人 */ export const SHAPE_ROUND = 'p'
// /** 合伙人 */ export const SHAPE_PARTNER = 'pt'
// /** 代持人 */ export const SHAPE_HOLDER = 'hd'

/** 实现边 */ export const SHAPE_SOLID_EDGE = 'se'
/** 虚线边 */ export const SHAPE_DOT_EDGE = 'de'
/** 文本框 */ export const SHAPE_TXT = 't'

const PORTS_DEF = [
    {group: 'top'},
    // {group: 'right'},
    {group: 'bottom'},
    // {group: 'left'}
]

const CIRCLE_DEF = {
    r: 4.5,
    magnet: true,
    strokeWidth: 1,
    visibility: 'hidden'
}

export const PORTS = {
    groups: {
        top: {position: 'top', allowMultiple: true, attrs: {circle: CIRCLE_DEF}},
        // right: {position: 'right', allowMultiple: true, attrs: {circle: CIRCLE_DEF}},
        bottom: {position: 'bottom', allowMultiple: true, attrs: {circle: CIRCLE_DEF}},
        // left: {position: 'left', allowMultiple: true, attrs: {circle: CIRCLE_DEF}}
    },
    items: PORTS_DEF
};

const ROUTER_ARGS = {
    startDirections: 'bottom', endDirections: 'top', padding: 15
};

const ENTITY_RECT = {
    inherit: 'rect', width: 140, height: 40, attrs: {body: {strokeWidth: 1}}, tools: ['node-editor'], ports: PORTS
}

const ENTITY_ROUND = {
    inherit: 'circle', width: 70, height: 70, attrs: {body: {strokeWidth: 1}}, tools: ['node-editor'], ports: PORTS
}

const ENTITY_EDGE = {
    inherit: 'edge', tools: ['vertices'], router: {name: 'manhattan', args: ROUTER_ARGS}
}

// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

/** "公司" 的定义 */ Graph.registerNode(SHAPE_RECT, ENTITY_RECT, true)
// /** "家族公司" 的节点定义 */ Graph.registerNode(SHAPE_FAMILY_COM, ENTITY_RECT, true)
// /** "平台公司" 的节点定义 */ Graph.registerNode(SHAPE_PLATFORM_COM, ENTITY_RECT, true)
// /** "其他投资板块公司" 的节点定义 */ Graph.registerNode(SHAPE_OTHER_COM, ENTITY_RECT, true)
// /** "投资方" 的节点定义 */ Graph.registerNode(SHAPE_INVESTOR, ENTITY_RECT, true)
// /** "员工持股平台" 的节点定义 */ Graph.registerNode(SHAPE_SHARE_HOLDER, ENTITY_RECT, true)
// /** "拟新设公司" 的节点定义 */ Graph.registerNode(SHAPE_NEW_COM, ENTITY_RECT, true)
// /** "拟注销公司" 的节点定义 */ Graph.registerNode(SHAPE_CANCELED_COM, ENTITY_RECT, true)

/** "自然人" 的定义 */ Graph.registerNode(SHAPE_ROUND, ENTITY_ROUND, true)
// /** "合伙人" 的节点定义 */ Graph.registerNode(SHAPE_PARTNER, ENTITY_ROUND, true)
// /** "代持人" 的节点定义 */ Graph.registerNode(SHAPE_HOLDER, ENTITY_ROUND, true)

/** "实线边" 的定义 */ Graph.registerEdge(SHAPE_SOLID_EDGE, {...ENTITY_EDGE, attrs: {line: {strokeWidth: 1}}}, true)
/** "虚线边" 的定义 */ Graph.registerEdge(SHAPE_DOT_EDGE, {...ENTITY_EDGE, attrs: {line: {strokeWidth: 1, strokeDasharray: '5 5'}}}, true)

/** "文本框" 的定义 */ Graph.registerNode(SHAPE_TXT, {
        inherit: 'rect', width: 120, height: 80,
        attrs: {body: {strokeWidth: 1, strokeDasharray: '5 5', rx: 5, ry: 5}, text: {fill: 'red'}},
        tools: ['node-editor']
    },
    true
)

load_external()  // ~ 加载外部定义

// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

export function prettify(data: any) {
    // ~ 动态调整 Edge 上的 label
    for (let idx = 0; idx < data.edges.length; idx++) {
        if (!data.edges[idx].labels) {
            continue
        }

        if (data.edges[idx].labels.length == 0) {
            continue
        }

        data.edges[idx].labels = [{
            attrs: {
                label: {
                    text: data.edges[idx].labels[0],
                }
            },
            position: {
                distance: LABEL_POSITION_DISTANCE
            }
        }]
    }

    // ~ 动态调整 Node 上的 Label
    for (let idx = 0; idx < data.nodes.length; idx++) {
        const node = data.nodes[idx];
        if (node.shape === SHAPE_ROUND) {
            // ~ 对于 "圆形" 的节点, 确保 Label 的 Auto-Wrap
            let wrappedLabel = ''
            const split: string[] = node.label.split(' ')
            const splitLength = split.length;
            for (let splitIdx = 0; splitIdx < splitLength; splitIdx++) {
                const currSplit = split[splitIdx]
                if (isNumeric(currSplit)) {
                    wrappedLabel += currSplit
                    if (splitIdx + 1 != splitLength) wrappedLabel += '\n'
                } else {
                    if (currSplit.length >= 5) {
                        wrappedLabel += currSplit.substring(0, 2) + '\n'
                        wrappedLabel += currSplit.substring(2)
                        if (splitIdx + 1 != splitLength) wrappedLabel += '\n'
                    } else {
                        wrappedLabel += currSplit
                        if (splitIdx + 1 != splitLength) wrappedLabel += '\n'
                    }
                }
            }
            data.nodes[idx].label = wrappedLabel
        }
    }
}

export function buildGraph(container: HTMLElement | undefined, state_fn: () => State) {
    const graph = new Graph({
        container: container,  // 画布的容器
        autoResize: true,      // 是否监听容器大小改变，并自动更新画布大小
        panning: true,         // 画布拖拽
        mousewheel: true,
        background: {color: '#F2F7FA'},

        connecting: {
            // @formatter:off
            allowBlank: false,  // 是否允许连接到画布空白位置的点，默认为 true
            allowLoop:  false,  // 是否允许创建循环连线，即边的起始节点和终止节点为同一节点，默认为 true
            allowNode:  false,  // 是否允许边连接到节点（非节点上的连接桩），默认为 true
            allowEdge:  false,  // 是否允许边连接到另一个边，默认为 true
            allowPort:   true,  // 是否允许边连接到连接桩，默认为 true
            allowMulti: false,  // 是否允许在相同的起始节点和终止之间创建多条边，默认为 true
            // @formatter:on

            createEdge(): Edge {
                const edge = graph.createEdge({
                    shape: state_fn().edgeShape,
                    labels: [{position: 0.2}]
                });
                edge.setRouter('manhattan', ROUTER_ARGS)
                return edge
            },
            validateConnection({sourceCell, targetCell}) {
                if (sourceCell && targetCell) {
                    return sourceCell.id !== targetCell.id; // 不允许连接自己
                }
                return false;
            },
        },
        grid: {
            visible: true, type: 'doubleMesh',
            args: [
                {
                    color: '#eee', // 主网格线颜色
                    thickness: 1, // 主网格线宽度
                },
                {
                    color: '#ddd', // 次网格线颜色
                    thickness: 1, // 次网格线宽度
                    factor: 4, // 主次网格线间隔
                },
            ],
        },
    })
    return graph
}

class SimpleNodeView extends NodeView {
    protected renderMarkup() {
        return this.renderJSONMarkup({
            tagName: 'rect',
            selector: 'body',
        })
    }

    update() {
        super.update({
            body: {
                refWidth: '100%',
                refHeight: '100%',
                fill: '#8f8f8f',
            },
        })
    }
}

/**
 * Description: 图形工具.
 * @author <a href="mailto:scorpio.caplike@gmail.com">LiKe</a> @ 2025-05-03 10:54:49
 */
export class GraphTools {
    private graphModal: Component;
    private graph: Graph;

    private static _offset: number = 8

    /**
     * 初始的 x, 用于在添加节点的时候, 提供一个坐标偏移 (以避免添加的节点重叠)
     * @private
     */
    private _x: number = GraphTools._offset

    /**
     * 初始的 y, 用于在添加节点的时候, 提供一个坐标偏移 (以避免添加的节点重叠)
     * @private
     */
    private _y: number = GraphTools._offset

    get x() {
        this._x += GraphTools._offset
        return this._x - GraphTools._offset
    }

    get y() {
        this._y += GraphTools._offset
        return this._y - GraphTools._offset
    }

    /**
     * Description: 显示/隐藏所有节点的连接桩.
     * @author <a href="mailto:scorpio.caplike@gmail.com">LiKe</a> @ 2025-05-03 17:51:34
     */
    private toggleNodePorts(state: string) {
        this.graph?.getNodes().forEach(node => {
            node.getPorts().forEach(port => {
                node.portProp(port.id!, 'attrs/circle/visibility', state)
            })
        })
    }

    /**
     * Description: 验证节点的持股比例.
     * @author <a href="mailto:scorpio.caplike@gmail.com">LiKe</a> @ 2025-05-03 18:50:46
     */
    private validateCellProportion(cell: Cell, proportion: number | null) {
        const connectedEdges = this.graph?.getConnectedEdges(cell);
        const proportions: number[] = []
        if (proportion) {
            proportions.push(proportion)
        }
        connectedEdges?.forEach((edge) => {
            let labels = edge.getLabels();
            if (labels.length == 0) {
                return
            }

            let labelText = labels[0].attrs?.label.text;
            if (!labelText) {
                return
            }
            let labelString = labelText as String
            if (labelString.endsWith('%')) {
                proportions.push(Number(labelString.substring(0, labelString.length - 1)))
            }
        })

        const overallProportion = proportions.reduce((prev, curr) => prev + curr, 0);
        if (overallProportion > 100) {
            throw Error(`持股比例不能超过 100% (当前: ${overallProportion}%)`)
        }
    }

    constructor(graphModal: Component, graph: Graph) {
        this.graphModal = graphModal
        this.graph = graph;

        this.graph
            .use(
                new Snapline({enabled: true})
            )
            .use(
                new History({enabled: true, ignoreChange: true})
            )
            .use(
                new Transform({
                    resizing: {
                        enabled: true,
                        // minWidth: 1, maxWidth: 400, minHeight: 1, maxHeight: 200,
                        orthogonal: false, restrict: false,
                        preserveAspectRatio: true, // 保持宽高比
                    }
                })
            )
            .use(
                new Export()
            )
        console.log('[GraphTools] Graph tools loaded.');
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    /**
     * Description: 注册画布事件.
     *
     * @return GraphTools 工具本身
     * @author <a href="mailto:scorpio.caplike@gmail.com">LiKe</a> @ 2025-05-03 17:42:52
     */
    registerEvents(): GraphTools {
        this.graph
            // ~ "节点" 事件
            .on('node:mouseover', () => this.toggleNodePorts('visible'))
            .on('node:mouseleave', () => this.toggleNodePorts('hidden'))
            .on('node:contextmenu', ({node}) => node.remove()) // 右键删除

            // ~ "边" 事件
            .on('edge:connected', ({edge}) => {
                // const sourceNode = this.graph?.getCellById(edge.getSourceCellId());
                // const targetNode = this.graph?.getCellById(edge.getTargetCellId());
                // if (!sourceNode || !targetNode) return
                //
                // const sourceShape = sourceNode.shape
                // const targetShape = targetNode.shape
                //
                // if (
                //     (sourceShape === SHAPE_PLATFORM_COM && targetShape === SHAPE_OTHER_COM) ||
                //     (sourceShape === SHAPE_INVESTOR && targetShape === SHAPE_PLATFORM_COM) ||
                //     (sourceShape === SHAPE_SHARE_HOLDER && targetShape === SHAPE_PLATFORM_COM)
                // ) edge.setAttrs({line: {strokeDasharray: '5 5'}})

                // // ~ Rule: 如果连线的目标节点是 "拟新设公司" 或者 "拟注销公司" - 使用虚线
                // if ([SHAPE_NEW_COM, SHAPE_CANCELED_COM].includes(targetShape)) {
                //     edge.setAttrs({line: {strokeDasharray: '5 5'}})
                // }

                // if (SHAPE_CANCELED_COM == targetShape) {
                //     edge.setLabels('X')  // ~ Rule: "拟注销公司" 的线中间打个叉
                // } else {
                //     graphModal.setState({proportionalEdge: edge, proportionalModalVisible: true})  // ~ Rule: 提示输入股权比例
                // }
                this.graphModal.setState({proportionalEdge: edge, proportionalModalVisible: true})  // ~ Rule: 提示输入股权比例
            })
            .on('edge:contextmenu', ({edge}) => edge.remove()) // ~ 右键删除

            // ~ "历史记录" 事件
            .on('history:change', () => {
                this.graphModal.setState({canRedo: this.graph.canRedo(), canUndo: this.graph.canUndo()})
            })

            .on('render:done', () => this.fit())

        console.log('[GraphTools] Graph events register successfully.');
        return this
    }

    /**
     * Description: 为边打上标签 (股权占比).
     * @author <a href="mailto:scorpio.caplike@gmail.com">LiKe</a> @ 2025-05-03 16:44:35
     */
    setEdgeLabel() {
        // @ts-ignore
        const {proportionalEdge, proportionalModalValue} = this.graphModal.state
        if (!proportionalEdge) {
            return
        }

        // ~ 执行验证
        try {
            this.validateCellProportion(this.graph?.getCellById((proportionalEdge as Edge).getTargetCellId()), proportionalModalValue);
        } catch (error) {
            if (error instanceof Error) {
                message.error(error.message).then(nth)
                return
            }
        }

        (proportionalEdge as Edge).setLabels({
            attrs: {
                label: {
                    text: proportionalModalValue + '%'
                }
            },
            position: {
                distance: LABEL_POSITION_DISTANCE
            }
        })

        // ~ 恢复现场
        this.graphModal.setState({
            proportionalEdge: null,
            proportionalModalVisible: false,
            proportionalModalValue: 100
        })
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    center() {
        this.graph?.centerContent()
    }

    fit() {
        setTimeout(() => {
            this.graph.zoomToRect(this.getRect(), {padding: 20})

            if (!this.graph.getPlugin('minimap')) {
                this.graph.use(
                    new MiniMap({
                        container: document.getElementById('minimap-container') as HTMLElement,
                        width: 200,
                        height: 140,
                        padding: 10,
                        graphOptions: {
                            createCellView(cell) {
                                // 可以返回三种类型数据
                                // 1. null: 不渲染
                                // 2. undefined: 使用 X6 默认渲染方式
                                // 3. CellView: 自定义渲染
                                if (cell.isEdge()) {
                                    return null
                                }
                                if (cell.isNode()) {
                                    return SimpleNodeView
                                }
                            },
                        },
                    })
                )
            }

            this.graphModal.setState({openTools: true}) // - 显示工具箱
            this.graphModal.setState({graphLoading: false})
        }, 500)
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    exportPic() {
        this.graph.exportPNG('graph', {
            padding: 5,
            quality: 1, width: 3840, height: 2160
        })
    }

    exportData() {
        // TODO
    }

    getPic(callback: (dataUri: string) => any) {
        this.graph.toPNG(callback, {
            padding: 5,
            quality: 1, width: 3840, height: 2160
        })
    }

    getData(): string {
        return JSON.stringify(this.graph.toJSON())
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    getRect() {
        const cells = this.graph.getCells();
        return this.graph.getCellsBBox(cells) as Rectangle
    }

    addRect = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_RECT, label: '公司'})
    // /** 手动添加 "主体公司" */ addCompany = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_RECT, label: '主体公司'})
    // /** 手动添加 "家族公司" */ addFamilyCompany = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_FAMILY_COM, label: '家族公司'})
    // /** 手动添加 "平台公司" */ addPlatformCompany = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_PLATFORM_COM, label: '平台公司'})
    // /** 手动添加 "其他投资板块公司" */ addOtherCompany = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_OTHER_COM, label: '其他投资板块公司'})
    // /** 手动添加 "投资方" */ addInvestor = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_INVESTOR, label: '投资方'})
    // /** 手动添加 "员工持股平台" */ addShareHolder = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_SHARE_HOLDER, label: '员工持股平台'})
    // /** 手动添加 "拟新设公司" */ addNewCompany = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_NEW_COM, label: '拟新设公司'})
    // /** 手动添加 "拟注销公司" */ addCanceledCompany = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_CANCELED_COM, label: '拟注销公司'})

    addRound = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_ROUND, label: '自然人'})
    // /** 手动添加 "合伙人" */ addPartner = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_PARTNER, label: '合伙人'})
    // /** 手动添加 "代持人" */ addHolder = () => this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_HOLDER, label: '指定代持人'})

    /** 手动添加 "文本框" */ addText = (label: string | null, x: number | null, y: number | null) => {
        if (label) {
            const labelArr = (label as string).split('|');
            const width = labelArr.reduce((a, b) => (a.length >= b.length ? a : b), '').length * 16
            const height = labelArr.length * 16 + (labelArr.length - 1) * 16 + 32

            label = labelArr.map(elem => elem.trim()).join('\n\n')
            this.graph?.addNode({
                x: x == null ? this.x : x - width, y: y == null ? this.y : y - height,
                shape: SHAPE_TXT, width: width, height: height,
                label: label == null ? '文本框' : label
            })
        } else {
            this.graph?.addNode({x: this.x, y: this.y, shape: SHAPE_TXT, label: '文本框'})
        }
    }
}
